package com.gaojinqi.sys.modular.rbac.controller;

import com.gaojinqi.base.common.cache.KvCache;
import com.gaojinqi.base.common.exception.AuthException;
import com.gaojinqi.base.common.exception.BizRuntimeException;
import com.gaojinqi.base.common.validation.group.Update;
import com.gaojinqi.base.common.vo.IResult;
import com.gaojinqi.base.common.vo.ResultVo;
import com.gaojinqi.sys.common.constant.ShiroDefaultConst;
import com.gaojinqi.sys.config.properties.JwtProperties;
import com.gaojinqi.sys.core.auth.util.LoginContextHolder;
import com.gaojinqi.sys.core.shiro.util.JwtTokenHelper;
import com.gaojinqi.sys.modular.rbac.model.LoginBo;
import com.gaojinqi.sys.modular.rbac.model.User;
import com.gaojinqi.sys.modular.rbac.service.UserService;
import com.gaojinqi.sys.modular.rbac.vo.LoginVo;
import com.gaojinqi.sys.modular.rbac.vo.RegisterVo;
import com.gaojinqi.sys.modular.rbac.vo.UserSaveVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import java.util.Map;
import java.util.Objects;

/**
 * 账号 - 控制层
 *
 * @author gaojinqi
 * @version 1.0
 * @since 2020年04月21日
 */
@Api(tags = "账号")
@RestController
@Validated
@Slf4j
public class AccountController {

    @Autowired
    private UserService userService;
    @Autowired
    private KvCache<String, String> cache;
    @Autowired
    private JwtTokenHelper jwtTokenHelper;

    @ApiOperation(value = "注册用户")
    @PostMapping("register")
    public IResult<User> register(@Validated RegisterVo param) {
        if (!Objects.equals(param.getPassword(), param.getRePassword())) {
            throw new BizRuntimeException("重复密码不一致");
        }
        if (userService.findByUserName(param.getUserName()) != null) {
            throw new BizRuntimeException("该用户名已被注册");
        }
        User user = new User();
        BeanUtils.copyProperties(param, user);
        userService.save(user);
        return ResultVo.suc(user);
    }

    @ApiOperation(value = "登录")
    @PostMapping("/login")
    public IResult<Map<String, Object>> login(@Validated LoginVo param, HttpServletResponse servletResponse) {
        Subject subject = getSubject();
        String userName = param.getUserName();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, param.getPassword());
        try {
            subject.login(token);
        } catch (UnknownAccountException uae) {
            log.error(uae.getMessage(), uae);
            throw new AuthException("未知账户！");
        } catch (LockedAccountException lae) {
            log.error(lae.getMessage(), lae);
            throw new AuthException("账户已锁定！");
        } catch (IncorrectCredentialsException iae) {
            log.error(iae.getMessage(), iae);
            throw new AuthException("密码错误！");
        } catch (ExcessiveAttemptsException eae) {
            log.error(eae.getMessage(), eae);
            throw new AuthException("用户名或密码错误次数太多！");
        } catch (AuthenticationException ae) {
            log.error(ae.getMessage(), ae);
            throw new AuthException("验证未通过！");
        }
        String jwt = jwtTokenHelper.createAndCacheToken(userName);
        return jwtTokenHelper.writeJwt(servletResponse, jwt, false);
    }

    @ApiOperation(value = "登出")
    @GetMapping("/logout")
    public IResult<Void> logout() {
        LoginContextHolder.getContext().logout();
        return ResultVo.suc("登出成功");
    }

    @ApiOperation(value = "查询当前用户信息")
    @GetMapping(value = "/account")
    public IResult<LoginBo> detail() {
        return ResultVo.suc(LoginContextHolder.getContext().getUser());
    }

    @ApiOperation(value = "修改当前用户")
    @PutMapping(value = "/account")
    public IResult<User> updateUser(@Validated(Update.class) @RequestBody UserSaveVo param) {
        String userId = LoginContextHolder.getContext().getUserId();
        User user = userService.findByUserName(param.getUserName());
        if (user != null && !Objects.equals(user.getUserId(), userId)) {
            throw new BizRuntimeException("该用户名已存在");
        }
        User entity = new User();
        entity.setUserId(userId);
        BeanUtils.copyProperties(param, entity);
        userService.update(entity);
        // 清空缓存
        LoginContextHolder.getContext().clearCache();
        return ResultVo.suc(entity);
    }

    @ApiOperation(value = "注销账户")
    @DeleteMapping(value = "/unsubscribe")
    public IResult<String> unsubscribe() {
        String userId = LoginContextHolder.getContext().getUserId();
        userService.deleteById(userId);
        LoginContextHolder.getContext().logout();
        return ResultVo.suc("注销成功");
    }

    private Subject getSubject() {
        return SecurityUtils.getSubject();
    }

}

